<html>
<body>
    <div><audio id="mp3Player" src="bgm.mp3" autoplay loop></audio></div>
    <div id="main"><canvas id="gameCanvas"  width="100" height="100"></canvas></div>
<script>
    const TOTAL_PLAYERS = 2;
    const PLAYER_1 = 0, PLAYER_2 = 1;
    const EMPTY = 0, RED = 1, GREEN = 2, ORANGE = 3, YELLOW = 4, CYAN = 5, PURPLE = 6, BLUE = 7;
    const SHARP_I = 0, SHARP_J = 1, SHARP_L = 2, SHARP_O = 3, SHARP_S = 4, SHARP_T = 5, SHARP_Z = 6;
    const ZERO_DEGREE = 0, NINETY_DEGREE = 90, ONE_EIGHTY_DEGREE = 180, TWO_SEVENTY_DEGREE = 270;
    const UP = 0, DOWN = 1, LEFT = 2, RIGHT = 3, MUTE = 4;
    const GAME_STATE_INITIAL = 1,
          GAME_STATE_PLAYING = 2,
          GAME_STATE_GAMEOVER = 3,
          GAME_STATE_PAUSING = 4;
    const GAMEBOARD_WIDTH = 10, GAMEBOARD_HEIGHT = 20;
    const NUMBER_OF_SQUARES_IN_SHAPE = 4;
    const SQUARE_DIMEMSION = 20;
    const GAMEBOARD_START = {"X": 160, "Y": 440};
    const SHAPE_INITIAL_X_INDEX = 3, SHAPE_INITIAL_Y_INDEX = 0;
    const NUMBER_OF_SHAPE_GENERATED = 50000;
    const FPS = 60;

    const LEVEL_GAME_UPDATE_TIME = [999999, 
        60, 55, 50, 45, 40, 
        35, 30, 25, 20, 15, 
        14, 13, 12, 11, 10, 
        9,  8,  7,  6,  5, 4, 3, 2, 1];

    const NEXT_LEVEL_REQUIRED_LINES = [0, 20, 20, 20, 20, 20,
        25, 25, 25, 25, 25,
        30, 30, 30, 30, 30,
        12, 12, 12, 12];

    function SHAPE()
    {
        this.x_index = new Array(NUMBER_OF_SQUARES_IN_SHAPE),
        this.y_index = new Array(NUMBER_OF_SQUARES_IN_SHAPE),
        this.color = null,
        this.rotate_degree = ZERO_DEGREE,
        this.type_of_shape = null;
    };

    var isDual = false
    var isPlayingMusic = false
    var players = [{"shape_count": -1, "next_level_line_count": 0, "update_game_time": 0, "level": 0, "lines": 0, "tetris": 0, "isGameover": false, "current_shape": null, "next_shape": null, "gameboard": null},
                   {"shape_count": -1, "next_level_line_count": 0, "update_game_time": 0, "level": 0, "lines": 0, "tetris": 0, "isGameover": false, "current_shape": null, "next_shape": null, "gameboard": null}]
    var current_game_state = GAME_STATE_INITIAL
    var setCurrentLevel = 1

    var canvas = document.getElementById('gameCanvas')
    var cxt = canvas.getContext("2d")
    var musicPlayer = document.getElementById("mp3Player")


    // UI Setting
    let windowWidth = window.innerWidth
    let windowHeight = window.innerHeight
    
    var w = 0
    var h = 0
    var cell = 0
    var cellhalf = 0
    function windowResize() {
        //窗口宽高
        let windowWidth = window.innerWidth
        let windowHeight = window.innerHeight

        body_w = windowWidth
        body_w = body_w % 9 == 0 ? body_w : body_w - body_w %9 //让坐标为整数
        if(body_w < (9 * 30)) body_w = 270
        else if(body_w > (9 * 70)) body_w = 630
        console.log(windowWidth + "|" + body_w)
        w = body_w/9*8 , h = w/8*9
        cellhalf = cell/2
        canvas.width = w + cell
        canvas.height = h + cell
        cxt.strokeStyle = "#ff0000"
        cxt.font=cell*0.6+"px 微软雅黑"
        //坐标点为中心，右下为正，左上为负
        //cxt.translate(whalf + cellhalf, hhalf + cellhalf)
        renewUI()
    };
    //windowResize()
    
    canvas.width = 800
    canvas.height = 600
    
    // 刷新UI的函数
    function renewUI(){
        var START_X = -1
        var START_Y = -1
    
        cxt.clearRect(0, 0, canvas.width, canvas.height)
        cxt.fillStyle = "rgba(64,21,96,1.0)"
        cxt.fillRect(0,0,canvas.width, canvas.height)
        
        if(current_game_state == GAME_STATE_INITIAL){
            cxt.fillStyle = "rgba(255,255,255,1.0)"
            cxt.font= "22px Arial"
            cxt.fillText("Press '1': Single Player ", 50, 570)
            cxt.fillText("Press '2': Two Players Dual", 50, 595)
            cxt.fillText("Press 'P': Pause when playing ", 380, 570)
            cxt.fillText("Press 'M': Play/Stop Tetris BGM", 380, 595)
        }
        
        if(current_game_state == GAME_STATE_INITIAL || current_game_state == GAME_STATE_GAMEOVER){
            cxt.fillStyle = "rgba(192,192,0,1.0)"
            cxt.font= "45px Times New Roman"
            cxt.fillText("Press 'Y' for new game", 200, 40)
            
            cxt.fillStyle = "rgba(100,200,230,1.0)"
            cxt.font= "38px Times New Roman"
            cxt.fillText("Level:", 10, 40)
            cxt.fillText(setCurrentLevel, 110, 40)
        }
        
        START_X = 20
        START_Y = 140
        cxt.beginPath();
        cxt.fillStyle="rgba(0,0,0,1.0)"
        cxt.fillRect(START_X, START_Y, 100, 60)
        cxt.lineWidth="3";
        cxt.strokeStyle = "rgba(190,190,190,1.0)"
        cxt.rect(START_X, START_Y, 100, 60)
        
        START_Y = 220
        cxt.lineWidth="1"
        cxt.fillStyle="rgba(0,0,0,1.0)"
        cxt.fillRect(START_X, START_Y, 100, 25)
        cxt.strokeStyle = "rgba(190,190,190,1.0)"
        cxt.rect(START_X, START_Y, 100, 25)
          
        cxt.fillStyle="rgba(0,0,0,1.0)"
        cxt.fillRect(START_X, START_Y + 25, 100, 25)
        cxt.strokeStyle = "rgba(190,190,190,1.0)"
        cxt.rect(START_X, START_Y + 25, 100, 25)
        
        cxt.fillStyle="rgba(0,0,0,1.0)"
        cxt.fillRect(START_X, START_Y + 50, 100, 25)
        cxt.strokeStyle = "rgba(190,190,190,1.0)"
        cxt.rect(START_X, START_Y + 50, 100, 25)
        cxt.stroke();
        
        START_X = 140
        START_Y = 100
        cxt.fillStyle = "rgba(0,0,0,1.0)"
        cxt.fillRect(START_X, START_Y, 240, 440)
        cxt.fillStyle = "rgba(190,190,190,1.0)"
        for(var i=START_Y; i<(START_Y + SQUARE_DIMEMSION * (GAMEBOARD_HEIGHT + 2)); i+=SQUARE_DIMEMSION) cxt.fillRect(START_X, i, SQUARE_DIMEMSION, SQUARE_DIMEMSION)
        for(var i=START_X; i<(START_X + SQUARE_DIMEMSION * (GAMEBOARD_WIDTH + 2)); i+=SQUARE_DIMEMSION)  cxt.fillRect(i, START_Y, SQUARE_DIMEMSION, SQUARE_DIMEMSION)
        for(var i=START_Y; i<(START_Y + SQUARE_DIMEMSION * (GAMEBOARD_HEIGHT + 2)); i+=SQUARE_DIMEMSION) cxt.fillRect(START_X + (GAMEBOARD_WIDTH + 1) * SQUARE_DIMEMSION, i, SQUARE_DIMEMSION, SQUARE_DIMEMSION)
        for(var i=START_X; i<(START_X + SQUARE_DIMEMSION * (GAMEBOARD_WIDTH + 2)); i+=SQUARE_DIMEMSION)  cxt.fillRect(i, START_Y + SQUARE_DIMEMSION * (GAMEBOARD_HEIGHT + 1), SQUARE_DIMEMSION, SQUARE_DIMEMSION)

        if(current_game_state == GAME_STATE_PLAYING || current_game_state == GAME_STATE_GAMEOVER)
        {
            START_X = 20
            START_Y = 220
            cxt.font= "20px Arial"
            cxt.fillStyle = "rgba(248,209,0,1.0)"
            cxt.fillText('level ', START_X + 5, START_Y + 20)
            cxt.font= "18px Arial"
            cxt.fillStyle = "rgba(255,255,255,1.0)"
            cxt.fillText(getFormatDisplayText(players[0].level), START_X + 48, START_Y + 20)
            cxt.font= "20px Arial"
            cxt.fillStyle = "rgba(248,209,0,1.0)"
            cxt.fillText('lines ', START_X + 5, START_Y + 45)
            cxt.font= "18px Arial"
            cxt.fillStyle = "rgba(255,255,255,1.0)"
            cxt.fillText(getFormatDisplayText(players[0].lines), START_X + 48, START_Y + 45)
            cxt.font= "20px Arial"
            cxt.fillStyle = "rgba(248,209,0,1.0)"
            cxt.fillText('tetris ', START_X + 5, START_Y + 70)
            cxt.font= "18px Arial"
            cxt.fillStyle = "rgba(255,255,255,1.0)"
            cxt.fillText(getFormatDisplayText(players[0].tetris), START_X + 48, START_Y + 70)
            
            START_X = 160
            START_Y = 120
            for(var i=0; i<players[0].gameboard.length; i++){
                for(var j=0; j<players[0].gameboard[i].length; j++){
                    cxt.fillStyle = getColorCode(players[0].gameboard[i][j])
                    cxt.fillRect(j * SQUARE_DIMEMSION + START_X, i * SQUARE_DIMEMSION + START_Y, SQUARE_DIMEMSION, SQUARE_DIMEMSION)
                }
            }
            
            cxt.fillStyle = getColorCode(players[0].current_shape.color)
            for(var i=0; i<NUMBER_OF_SQUARES_IN_SHAPE; i++)
            {
                cxt.fillRect(players[0].current_shape.x_index[i] * SQUARE_DIMEMSION + START_X, players[0].current_shape.y_index[i] * SQUARE_DIMEMSION + START_Y, SQUARE_DIMEMSION, SQUARE_DIMEMSION)
            }
            
            START_X = -30
            START_Y = 150
            cxt.fillStyle = getColorCode(players[0].next_shape.color)
            for(var i=0; i<NUMBER_OF_SQUARES_IN_SHAPE; i++)
            {
                cxt.fillRect(players[0].next_shape.x_index[i] * SQUARE_DIMEMSION + START_X, players[0].next_shape.y_index[i] * SQUARE_DIMEMSION + START_Y, SQUARE_DIMEMSION, SQUARE_DIMEMSION)
            }
            
            cxt.stroke();
        }
            
        if(isDual){
            START_X = 680
            START_Y = 140
            cxt.beginPath();
            cxt.fillStyle="rgba(0,0,0,1.0)"
            cxt.fillRect(START_X, START_Y, 100, 60)
            cxt.lineWidth="3";
            cxt.strokeStyle = "rgba(190,190,190,1.0)"
            cxt.rect(START_X, START_Y, 100, 60)
            
            START_Y = 220
            cxt.lineWidth="1"
            cxt.fillStyle="rgba(0,0,0,1.0)"
            cxt.fillRect(START_X, START_Y, 100, 25)
            cxt.strokeStyle = "rgba(190,190,190,1.0)"
            cxt.rect(START_X, START_Y, 100, 25)
              
            cxt.fillStyle="rgba(0,0,0,1.0)"
            cxt.fillRect(START_X, START_Y + 25, 100, 25)
            cxt.strokeStyle = "rgba(190,190,190,1.0)"
            cxt.rect(START_X, START_Y + 25, 100, 25)
            
            cxt.fillStyle="rgba(0,0,0,1.0)"
            cxt.fillRect(START_X, START_Y + 50, 100, 25)
            cxt.strokeStyle = "rgba(190,190,190,1.0)"
            cxt.rect(START_X, START_Y + 50, 100, 25)
            cxt.stroke();
            
            START_X = 420
            START_Y = 100
            cxt.fillStyle = "rgba(0,0,0,1.0)"
            cxt.fillRect(START_X, START_Y, 240, 440)
            cxt.fillStyle = "rgba(190,190,190,1.0)"
            for(var i=START_Y; i<(START_Y + SQUARE_DIMEMSION * (GAMEBOARD_HEIGHT + 2)); i+=SQUARE_DIMEMSION) cxt.fillRect(START_X, i, SQUARE_DIMEMSION, SQUARE_DIMEMSION)
            for(var i=START_X; i<(START_X + SQUARE_DIMEMSION * (GAMEBOARD_WIDTH + 2)); i+=SQUARE_DIMEMSION)  cxt.fillRect(i, START_Y, SQUARE_DIMEMSION, SQUARE_DIMEMSION)
            for(var i=START_Y; i<(START_Y + SQUARE_DIMEMSION * (GAMEBOARD_HEIGHT + 2)); i+=SQUARE_DIMEMSION) cxt.fillRect(START_X + (GAMEBOARD_WIDTH + 1) * SQUARE_DIMEMSION, i, SQUARE_DIMEMSION, SQUARE_DIMEMSION)
            for(var i=START_X; i<(START_X + SQUARE_DIMEMSION * (GAMEBOARD_WIDTH + 2)); i+=SQUARE_DIMEMSION)  cxt.fillRect(i, START_Y + SQUARE_DIMEMSION * (GAMEBOARD_HEIGHT + 1), SQUARE_DIMEMSION, SQUARE_DIMEMSION)

            if(current_game_state == GAME_STATE_PLAYING || current_game_state == GAME_STATE_GAMEOVER)
            {
                START_X = 680
                START_Y = 220
                cxt.font= "20px Arial"
                cxt.fillStyle = "rgba(248,209,0,1.0)"
                cxt.fillText('level ', START_X + 5, START_Y + 20)
                cxt.font= "18px Arial"
                cxt.fillStyle = "rgba(255,255,255,1.0)"
                cxt.fillText(getFormatDisplayText(players[1].level), START_X + 48, START_Y + 20)
                cxt.font= "20px Arial"
                cxt.fillStyle = "rgba(248,209,0,1.0)"
                cxt.fillText('lines ', START_X + 5, START_Y + 45)
                cxt.font= "18px Arial"
                cxt.fillStyle = "rgba(255,255,255,1.0)"
                cxt.fillText(getFormatDisplayText(players[1].lines), START_X + 48, START_Y + 45)
                cxt.font= "20px Arial"
                cxt.fillStyle = "rgba(248,209,0,1.0)"
                cxt.fillText('tetris ', START_X + 5, START_Y + 70)
                cxt.font= "18px Arial"
                cxt.fillStyle = "rgba(255,255,255,1.0)"
                cxt.fillText(getFormatDisplayText(players[1].tetris), START_X + 48, START_Y + 70)
                
                START_X = 440
                START_Y = 120
                for(var i=0; i<players[1].gameboard.length; i++){
                    for(var j=0; j<players[1].gameboard[i].length; j++){
                        cxt.fillStyle = getColorCode(players[1].gameboard[i][j])
                        cxt.fillRect(j * SQUARE_DIMEMSION + START_X, i * SQUARE_DIMEMSION + START_Y, SQUARE_DIMEMSION, SQUARE_DIMEMSION)
                    }
                }
                
                cxt.fillStyle = getColorCode(players[1].current_shape.color)
                for(var i=0; i<NUMBER_OF_SQUARES_IN_SHAPE; i++)
                {
                    cxt.fillRect(players[1].current_shape.x_index[i] * SQUARE_DIMEMSION + START_X, players[1].current_shape.y_index[i] * SQUARE_DIMEMSION + START_Y, SQUARE_DIMEMSION, SQUARE_DIMEMSION)
                }
                
                START_X = 630
                START_Y = 150
                cxt.fillStyle = getColorCode(players[0].next_shape.color)
                for(var i=0; i<NUMBER_OF_SQUARES_IN_SHAPE; i++)
                {
                    cxt.fillRect(players[1].next_shape.x_index[i] * SQUARE_DIMEMSION + START_X, players[1].next_shape.y_index[i] * SQUARE_DIMEMSION + START_Y, SQUARE_DIMEMSION, SQUARE_DIMEMSION)
                }
                
                cxt.stroke()
            }
        }
        
        if(players[PLAYER_1].isGameover){
            cxt.font= "40px Arial"
            cxt.fillStyle = "rgba(255,255,255,1.0)"
            cxt.fillText("Game Over", 150, 90)
        }
        if(players[PLAYER_2].isGameover){
            cxt.font= "40px Arial"
            cxt.fillStyle = "rgba(255,255,255,1.0)"
            cxt.fillText("Game Over", 430, 90)
        }
        
        if(current_game_state == GAME_STATE_PAUSING){
            cxt.font= "72px Arial"
            cxt.fillStyle = "rgba(255,255,255,1.0)"
            cxt.fillText("Pausing", 260, 320)
        }
    };
    
    function getFormatDisplayText(givenData){
        if(givenData < 10)            return '        ' + givenData
        else if (givenData < 100)     return '      ' + givenData
        else if (givenData < 1000)    return '    ' + givenData
        else if (givenData < 10000)   return '  ' + givenData
        else if (givenData >= 10000)  return '' + givenData
        else return 'Error'
    };

    function init_gameboard()
    {
        var board = new Array(GAMEBOARD_HEIGHT);
        for(var i=0; i<GAMEBOARD_HEIGHT; i++){
            board[i] = [];
            for(var j=0; j<GAMEBOARD_WIDTH; j++)
                board[i].push(EMPTY);
        }
        return board;
    };
    
    function getColorCode(givenColor){
        //EMPTY = 0, RED = 1, GREEN = 2, ORANGE = 3, YELLOW = 4, CYAN = 5, PURPLE = 6, BLUE = 7;
        switch(givenColor){
            case 0: return "#000000"
            case 1: return "#FF0000"
            case 2: return "#00FF00"
            case 3: return "#FF6600"
            case 4: return "#FFFF00"
            case 5: return "#66CCFF"
            case 6: return "#CC00FF"
            case 7: return "#0000FF"
            default: alert("Error Color Code is Given!")
        }
    };
    
    function getRandomIntInclusive(min, max)
    {
        min = Math.ceil(min);
        max = Math.floor(max);
        return Math.floor(Math.random() * (max - min + 1)) + min; //The maximum is inclusive and the minimum is inclusive 
    };
    
    var update_frame_time = 0;
    var update_count_down_time_frame = 60;
    var current_count_second = -1;
    
    var index_array = []; 
    for(var i=0; i<TOTAL_PLAYERS; i++) index_array.push(i);
    
    var shape_array = new Array(NUMBER_OF_SHAPE_GENERATED);
    function shapesGenerator()
    {
        for(var i=0; i < NUMBER_OF_SHAPE_GENERATED; i++)
            shape_array[i] = { color: getRandomIntInclusive(1,7), shape: getRandomIntInclusive(0,6) };
    };
    
    function init_playerData(){
        for(var i=0; i<TOTAL_PLAYERS; i++) players[i] = {"shape_count": 0, "next_level_line_count": 0, "update_game_time": 0, "level": setCurrentLevel, "lines": 0, "tetris": 0, "isGameover": false, "current_shape": createShapeSquare(shape_array[0]), "next_shape": createShapeSquare(shape_array[1]), "gameboard": init_gameboard()}
    };
    
    // =========================== KEYBOARD EVENT ==============================

    document.onkeydown = function(event){
        if (event.keyCode === 87)     { if(isDual) keyHandler(PLAYER_2, UP); else keyHandler(PLAYER_1, UP) }        // W
        else if(event.keyCode === 83) { if(isDual) keyHandler(PLAYER_2, DOWN); else keyHandler(PLAYER_1, DOWN) }    // S
        else if(event.keyCode === 65) { if(isDual) keyHandler(PLAYER_2, LEFT); else keyHandler(PLAYER_1, LEFT) }    // A
        else if(event.keyCode === 68) { if(isDual) keyHandler(PLAYER_2, RIGHT); else keyHandler(PLAYER_1, RIGHT) }  // D
        if(event.keyCode === 38) keyHandler(PLAYER_1, UP)    // UP
        else if(event.keyCode === 40) keyHandler(PLAYER_1, DOWN)    // DOWN
        else if(event.keyCode === 37) keyHandler(PLAYER_1, LEFT)    // LEFT
        else if(event.keyCode === 39) keyHandler(PLAYER_1, RIGHT)   // RIGHT
        if(event.keyCode === 49) setDualGame(false) // 1
        else if(event.keyCode === 50) setDualGame(true) // 2
        else if(event.keyCode === 220) changeLevel() // \
        else if(event.keyCode === 80) pauseGame() // P
        else if(event.keyCode === 89) startGame() // Y
        else if(event.keyCode === 77) playPauseMusic() // M
    }
    
    function setDualGame(isDualGame){
        if(current_game_state == GAME_STATE_INITIAL || current_game_state == GAME_STATE_GAMEOVER) isDual = isDualGame 
    };
    
    function startGame(){
        shapesGenerator()
        init_playerData()
        current_game_state = GAME_STATE_PLAYING
    };
    
    function pauseGame(){
        if(current_game_state == GAME_STATE_PLAYING) current_game_state = GAME_STATE_PAUSING
        else if(current_game_state == GAME_STATE_PAUSING) current_game_state = GAME_STATE_PLAYING
    };
    
    function playPauseMusic() {
        if (isPlayingMusic) {
          musicPlayer.pause()
        } else {
          musicPlayer.currentTime = 0
          musicPlayer.play()
        }
        isPlayingMusic = !isPlayingMusic
    };
    
    function changeLevel() {
        if(current_game_state == GAME_STATE_INITIAL || current_game_state == GAME_STATE_GAMEOVER){
            if(++setCurrentLevel > 24) setCurrentLevel = 1
        }
    };
    
    function keyHandler(playerIndex, keyIndex){
        if(current_game_state == GAME_STATE_PLAYING)
        {
            if(!players[playerIndex].isGameover)
            {
                var gameboard = players[playerIndex].gameboard;
                var current_shape = players[playerIndex].current_shape;
                
                if(keyIndex == UP)
                {
                    switch(current_shape.type_of_shape)
                    {
                        case SHARP_I:
                        {
                            var x = current_shape.x_index[1], y = current_shape.y_index[1];  // the pivot index of all shapes is 1
                            
                            /*****************
                             *   *   *   *   *
                             * 0 * 1 * 2 * 3 *
                             *   *   *   *   *
                             *****************/
                            if(current_shape.rotate_degree == ZERO_DEGREE)
                            {
                                if( (y-1) >= 0 && (y+2) < GAMEBOARD_HEIGHT )
                                {
                                    if(gameboard[y-1][x] == EMPTY && gameboard[y+1][x] == EMPTY && gameboard[y+2][x] == EMPTY)
                                    {
                                        current_shape.x_index[0] = current_shape.x_index[0] + 1; current_shape.y_index[0] = current_shape.y_index[0] - 1;
                                        current_shape.x_index[1] = current_shape.x_index[1] + 0; current_shape.y_index[1] = current_shape.y_index[1] + 0;
                                        current_shape.x_index[2] = current_shape.x_index[2] - 1; current_shape.y_index[2] = current_shape.y_index[2] + 1;
                                        current_shape.x_index[3] = current_shape.x_index[3] - 2; current_shape.y_index[3] = current_shape.y_index[3] + 2;
                                        current_shape.rotate_degree = NINETY_DEGREE;
                                    }
                                }
                            }
                            /*****
                             *   *
                             * 0 *
                             *   *
                             *****
                             *   *
                             * 1 *
                             *   *
                             *****
                             *   *
                             * 2 *
                             *   *
                             *****
                             *   *
                             * 3 *
                             *   *
                             *****/
                            else if(current_shape.rotate_degree == NINETY_DEGREE)
                            {
                                if( (x-1) >= 0 && (x+2) < GAMEBOARD_WIDTH )
                                if(gameboard[y][x-1] == EMPTY && gameboard[y][x+1] == EMPTY && gameboard[y][x+2] == EMPTY)
                                {
                                    current_shape.x_index[0] = current_shape.x_index[0] - 1; current_shape.y_index[0] = current_shape.y_index[0] + 1;
                                    current_shape.x_index[1] = current_shape.x_index[1] + 0; current_shape.y_index[1] = current_shape.y_index[1] + 0;
                                    current_shape.x_index[2] = current_shape.x_index[2] + 1; current_shape.y_index[2] = current_shape.y_index[2] - 1;
                                    current_shape.x_index[3] = current_shape.x_index[3] + 2; current_shape.y_index[3] = current_shape.y_index[3] - 2;
                                    current_shape.rotate_degree = ZERO_DEGREE;
                                }
                            }
                            /*****************
                             *   *   *   *   *
                             * 0 * 1 * 2 * 3 *
                             *   *   *   *   *
                             *****************/
                            else console.log("ERROR in I shape: No such rotating degree!");
                            
                            break;
                        }
                        case SHARP_J:
                        {
                            var x = current_shape.x_index[1], y = current_shape.y_index[1];
                            
                            /*****
                             *   *
                             * 3 *
                             *   *
                             *************
                             *   *   *   *
                             * 0 * 1 * 2 *
                             *   *   *   *
                             *************/
                            if(current_shape.rotate_degree == ZERO_DEGREE)
                            {
                                if( (y-1) >= 0 && (y+1) < GAMEBOARD_HEIGHT && (x+1) < GAMEBOARD_WIDTH )
                                {
                                    if(gameboard[y-1][x] == EMPTY && gameboard[y-1][x+1] == EMPTY && gameboard[y+1][x] == EMPTY)
                                    {
                                        current_shape.x_index[0] = current_shape.x_index[0] + 1; current_shape.y_index[0] = current_shape.y_index[0] - 1;
                                        current_shape.x_index[1] = current_shape.x_index[1] + 0; current_shape.y_index[1] = current_shape.y_index[1] + 0;
                                        current_shape.x_index[2] = current_shape.x_index[2] - 1; current_shape.y_index[2] = current_shape.y_index[2] + 1;
                                        current_shape.x_index[3] = current_shape.x_index[3] + 2; current_shape.y_index[3] = current_shape.y_index[3] + 0;
                                        current_shape.rotate_degree = NINETY_DEGREE;
                                    }
                                }
                            }
                            /*********
                             *   *   *
                             * 0 * 3 *
                             *   *   *
                             *********
                             *   *
                             * 1 *
                             *   *
                             *****
                             *   *
                             * 2 *
                             *   *
                             *****/
                            else if(current_shape.rotate_degree == NINETY_DEGREE)
                            {
                                if( (x-1) >= 0 && (x+1) < GAMEBOARD_WIDTH && (y+1) < GAMEBOARD_HEIGHT )
                                if(gameboard[y][x-1] == EMPTY && gameboard[y][x+1] == EMPTY && gameboard[y+1][x+1] == EMPTY)
                                {
                                    current_shape.x_index[0] = current_shape.x_index[0] + 1; current_shape.y_index[0] = current_shape.y_index[0] + 1;
                                    current_shape.x_index[1] = current_shape.x_index[1] + 0; current_shape.y_index[1] = current_shape.y_index[1] + 0;
                                    current_shape.x_index[2] = current_shape.x_index[2] - 1; current_shape.y_index[2] = current_shape.y_index[2] - 1;
                                    current_shape.x_index[3] = current_shape.x_index[3] + 0; current_shape.y_index[3] = current_shape.y_index[3] + 2;
                                    current_shape.rotate_degree = ONE_EIGHTY_DEGREE;
                                }
                            }
                            /*************
                             *   *   *   *
                             * 2 * 1 * 0 *
                             *   *   *   *
                             *************
                                     *   *
                                     * 3 *
                                     *   *
                                     *****/
                            else if(current_shape.rotate_degree == ONE_EIGHTY_DEGREE)
                            {
                                if( (y-1) >= 0 && (y+1) < GAMEBOARD_HEIGHT && (x-1) >= 0 )
                                {
                                    if(gameboard[y-1][x] == EMPTY && gameboard[y+1][x] == EMPTY && gameboard[y+1][x-1] == EMPTY)
                                    {
                                        current_shape.x_index[0] = current_shape.x_index[0] - 1; current_shape.y_index[0] = current_shape.y_index[0] + 1;
                                        current_shape.x_index[1] = current_shape.x_index[1] + 0; current_shape.y_index[1] = current_shape.y_index[1] + 0;
                                        current_shape.x_index[2] = current_shape.x_index[2] + 1; current_shape.y_index[2] = current_shape.y_index[2] - 1;
                                        current_shape.x_index[3] = current_shape.x_index[3] - 2; current_shape.y_index[3] = current_shape.y_index[3] + 0;
                                        current_shape.rotate_degree = TWO_SEVENTY_DEGREE;
                                    }
                                }
                            }
                            /*   *****
                                 *   *
                                 * 2 *
                                 *   *
                                 *****
                                 *   *
                                 * 1 *
                                 *   *
                             *********
                             *   *   *
                             * 3 * 0 *
                             *   *   *
                             *********/
                            else if(current_shape.rotate_degree == TWO_SEVENTY_DEGREE)
                            {
                                if( (y-1) >= 0 && (y+1) < GAMEBOARD_HEIGHT && (x-1) >= 0 && (x+1) < GAMEBOARD_WIDTH )
                                {
                                    if(gameboard[y][x-1] == EMPTY && gameboard[y-1][x-1] == EMPTY && gameboard[y+1][x+1] == EMPTY)
                                    {
                                        current_shape.x_index[0] = current_shape.x_index[0] - 1; current_shape.y_index[0] = current_shape.y_index[0] - 1;
                                        current_shape.x_index[1] = current_shape.x_index[1] + 0; current_shape.y_index[1] = current_shape.y_index[1] + 0;
                                        current_shape.x_index[2] = current_shape.x_index[2] + 1; current_shape.y_index[2] = current_shape.y_index[2] + 1;
                                        current_shape.x_index[3] = current_shape.x_index[3] + 0; current_shape.y_index[3] = current_shape.y_index[3] - 2;
                                        current_shape.rotate_degree = ZERO_DEGREE;
                                    }
                                }
                            }
                            /*****
                             *   *
                             * 3 *
                             *   *
                             *************
                             *   *   *   *
                             * 0 * 1 * 2 *
                             *   *   *   *
                             *************/
                            else console.log("ERROR in J shape: No such rotating degree!");
                            break;
                        }
                        case SHARP_L:
                        {
                            var x = current_shape.x_index[1], y = current_shape.y_index[1];
                            
                            /*       *****
                                     *   *
                                     * 3 *
                                     *   *
                             *************
                             *   *   *   *
                             * 0 * 1 * 2 *
                             *   *   *   *
                             *************/
                            if(current_shape.rotate_degree == ZERO_DEGREE)
                            {
                                if( (y-1) >= 0 && (y+1) < GAMEBOARD_HEIGHT && (x+1) < GAMEBOARD_WIDTH )
                                {
                                    if(gameboard[y-1][x] == EMPTY && gameboard[y+1][x] == EMPTY && gameboard[y+1][x+1] == EMPTY)
                                    {
                                        current_shape.x_index[0] = current_shape.x_index[0] + 1; current_shape.y_index[0] = current_shape.y_index[0] - 1;
                                        current_shape.x_index[1] = current_shape.x_index[1] + 0; current_shape.y_index[1] = current_shape.y_index[1] + 0;
                                        current_shape.x_index[2] = current_shape.x_index[2] - 1; current_shape.y_index[2] = current_shape.y_index[2] + 1;
                                        current_shape.x_index[3] = current_shape.x_index[3] + 0; current_shape.y_index[3] = current_shape.y_index[3] + 2;
                                        current_shape.rotate_degree = NINETY_DEGREE;
                                    }
                                }
                            }
                            /*****
                             *   *
                             * 0 *
                             *   *
                             *****
                             *   *
                             * 1 *
                             *   *
                             *********
                             *   *   *
                             * 2 * 3 *
                             *   *   *
                             *********/
                            else if(current_shape.rotate_degree == NINETY_DEGREE)
                            {
                                if( (y+1) < GAMEBOARD_HEIGHT && (x-1) >= 0 && (x+1) < GAMEBOARD_WIDTH )
                                {
                                    if(gameboard[y][x-1] == EMPTY && gameboard[y+1][x-1] == EMPTY && gameboard[y][x+1] == EMPTY)
                                    {
                                        current_shape.x_index[0] = current_shape.x_index[0] + 1; current_shape.y_index[0] = current_shape.y_index[0] + 1;
                                        current_shape.x_index[1] = current_shape.x_index[1] + 0; current_shape.y_index[1] = current_shape.y_index[1] + 0;
                                        current_shape.x_index[2] = current_shape.x_index[2] - 1; current_shape.y_index[2] = current_shape.y_index[2] - 1;
                                        current_shape.x_index[3] = current_shape.x_index[3] - 2; current_shape.y_index[3] = current_shape.y_index[3] + 0;
                                        current_shape.rotate_degree = ONE_EIGHTY_DEGREE;
                                    }
                                }
                            }
                            /*************
                             *   *   *   *
                             * 2 * 1 * 0 *
                             *   *   *   *
                             *************
                             *   *
                             * 3 *
                             *   *
                             *****/
                            else if(current_shape.rotate_degree == ONE_EIGHTY_DEGREE)
                            {
                                if( (y-1) >= 0 && (y+1) < GAMEBOARD_HEIGHT && (x-1) >= 0 )
                                {
                                    if(gameboard[y-1][x] == EMPTY && gameboard[y-1][x-1] == EMPTY && gameboard[y+1][x] == EMPTY)
                                    {
                                        current_shape.x_index[0] = current_shape.x_index[0] - 1; current_shape.y_index[0] = current_shape.y_index[0] + 1;
                                        current_shape.x_index[1] = current_shape.x_index[1] + 0; current_shape.y_index[1] = current_shape.y_index[1] + 0;
                                        current_shape.x_index[2] = current_shape.x_index[2] + 1; current_shape.y_index[2] = current_shape.y_index[2] - 1;
                                        current_shape.x_index[3] = current_shape.x_index[3] + 0; current_shape.y_index[3] = current_shape.y_index[3] - 2;
                                        current_shape.rotate_degree = TWO_SEVENTY_DEGREE;
                                    }
                                }
                            }
                            /*********
                             *   *   *
                             * 3 * 2 *
                             *   *   *
                             *********
                                 *   *
                                 * 1 *
                                 *   *
                                 *****
                                 *   *
                                 * 0 *
                                 *   *
                                 *****/
                            else if(current_shape.rotate_degree == TWO_SEVENTY_DEGREE)
                            {
                                if( (y-1) >= 0 && (x-1) >= 0 && (x+1) < GAMEBOARD_WIDTH )
                                {
                                    if(gameboard[y][x-1] == EMPTY && gameboard[y][x+1] == EMPTY && gameboard[y-1][x+1] == EMPTY)
                                    {
                                        current_shape.x_index[0] = current_shape.x_index[0] - 1; current_shape.y_index[0] = current_shape.y_index[0] - 1;
                                        current_shape.x_index[1] = current_shape.x_index[1] + 0; current_shape.y_index[1] = current_shape.y_index[1] + 0;
                                        current_shape.x_index[2] = current_shape.x_index[2] + 1; current_shape.y_index[2] = current_shape.y_index[2] + 1;
                                        current_shape.x_index[3] = current_shape.x_index[3] + 2; current_shape.y_index[3] = current_shape.y_index[3] + 0;
                                        current_shape.rotate_degree = ZERO_DEGREE;
                                    }
                                }
                            }
                            /*       *****
                                     *   *
                                     * 3 *
                                     *   *
                             *************
                             *   *   *   *
                             * 0 * 1 * 2 *
                             *   *   *   *
                             *************/
                            else console.log("ERROR in L shape: No such rotating degree!");
                            break;
                        }
                        case SHARP_O:
                        {
                            // nothing needs to change since it is a square
                            /*********
                             *   *   *
                             * 0 * 1 *
                             *   *   *
                             *********
                             *   *   *
                             * 2 * 3 *
                             *   *   *
                             *********/
                            break;
                        }
                        case SHARP_S:
                        {
                            var x = current_shape.x_index[1], y = current_shape.y_index[1];
                            /*   *********
                                 *   *   *
                                 * 2 * 3 *
                                 *   *   *
                             *************
                             *   *   *
                             * 0 * 1 *
                             *   *   *
                             *********   */
                            if(current_shape.rotate_degree == ZERO_DEGREE)
                            {
                                if( (y+1) < GAMEBOARD_HEIGHT && (x+1) < GAMEBOARD_WIDTH )
                                {
                                    if(gameboard[y][x+1] == EMPTY && gameboard[y+1][x+1] == EMPTY)
                                    {
                                        current_shape.x_index[0] = current_shape.x_index[0] + 1; current_shape.y_index[0] = current_shape.y_index[0] - 1;
                                        current_shape.x_index[1] = current_shape.x_index[1] + 0; current_shape.y_index[1] = current_shape.y_index[1] + 0;
                                        current_shape.x_index[2] = current_shape.x_index[2] + 1; current_shape.y_index[2] = current_shape.y_index[2] + 1;
                                        current_shape.x_index[3] = current_shape.x_index[3] + 0; current_shape.y_index[3] = current_shape.y_index[3] + 2;
                                        current_shape.rotate_degree = NINETY_DEGREE;
                                    }
                                }
                            }
                            /*****
                             *   *
                             * 0 *
                             *   *
                             *********
                             *   *   *
                             * 1 * 2 *
                             *   *   *
                             *********
                                 *   *
                                 * 3 *
                                 *   *
                                 *****/
                            else if(current_shape.rotate_degree == NINETY_DEGREE)
                            {
                                if( (y-1) >= 0 && (x-1) >= 0 && (x+1) < GAMEBOARD_WIDTH )
                                {
                                    if(gameboard[y][x-1] == EMPTY && gameboard[y-1][x+1] == EMPTY)
                                    {
                                        current_shape.x_index[0] = current_shape.x_index[0] - 1; current_shape.y_index[0] = current_shape.y_index[0] + 1;
                                        current_shape.x_index[1] = current_shape.x_index[1] + 0; current_shape.y_index[1] = current_shape.y_index[1] + 0;
                                        current_shape.x_index[2] = current_shape.x_index[2] - 1; current_shape.y_index[2] = current_shape.y_index[2] - 1;
                                        current_shape.x_index[3] = current_shape.x_index[3] + 0; current_shape.y_index[3] = current_shape.y_index[3] - 2;
                                        current_shape.rotate_degree = ZERO_DEGREE;
                                    }
                                }
                            }
                            /*   *********
                                 *   *   *
                                 * 2 * 3 *
                                 *   *   *
                             *************
                             *   *   *
                             * 0 * 1 *
                             *   *   *
                             *********   */
                            else console.log("ERROR in S shape: No such rotating degree!");
                            break;
                        }
                        case SHARP_T:
                        {
                            var x = current_shape.x_index[1], y = current_shape.y_index[1];
                            /*   *****
                                 *   *
                                 * 3 *
                                 *   *
                             *************
                             *   *   *   *
                             * 0 * 1 * 2 *
                             *   *   *   *
                             *************/
                            if(current_shape.rotate_degree == ZERO_DEGREE)
                            {
                                if( (y+1) < GAMEBOARD_HEIGHT )
                                {
                                    if(gameboard[y+1][x] == EMPTY)
                                    {
                                        current_shape.x_index[0] = current_shape.x_index[0] + 1; current_shape.y_index[0] = current_shape.y_index[0] - 1;
                                        current_shape.x_index[1] = current_shape.x_index[1] + 0; current_shape.y_index[1] = current_shape.y_index[1] + 0;
                                        current_shape.x_index[2] = current_shape.x_index[2] - 1; current_shape.y_index[2] = current_shape.y_index[2] + 1;
                                        current_shape.x_index[3] = current_shape.x_index[3] + 1; current_shape.y_index[3] = current_shape.y_index[3] + 1;
                                        current_shape.rotate_degree = NINETY_DEGREE;
                                    }
                                }
                            }
                            /*****
                             *   *
                             * 0 *
                             *   *
                             *********
                             *   *   *
                             * 1 * 3 *
                             *   *   *
                             *********
                             *   *
                             * 2 *
                             *   *
                             *****/
                            else if(current_shape.rotate_degree == NINETY_DEGREE)
                            {
                                if( (x-1) >= 0 )
                                {
                                    if(gameboard[y][x-1] == EMPTY)
                                    {
                                        current_shape.x_index[0] = current_shape.x_index[0] + 1; current_shape.y_index[0] = current_shape.y_index[0] + 1;
                                        current_shape.x_index[1] = current_shape.x_index[1] + 0; current_shape.y_index[1] = current_shape.y_index[1] + 0;
                                        current_shape.x_index[2] = current_shape.x_index[2] - 1; current_shape.y_index[2] = current_shape.y_index[2] - 1;
                                        current_shape.x_index[3] = current_shape.x_index[3] - 1; current_shape.y_index[3] = current_shape.y_index[3] + 1;
                                        current_shape.rotate_degree = ONE_EIGHTY_DEGREE;
                                    }
                                }
                            }
                            /*************
                             *   *   *   *
                             * 2 * 1 * 0 *
                             *   *   *   *
                             *************
                                 *   *
                                 * 3 *
                                 *   *
                                 *****/
                            else if(current_shape.rotate_degree == ONE_EIGHTY_DEGREE)
                            {
                                if( (y-1) >= 0 )
                                {
                                    if(gameboard[y-1][x] == EMPTY)
                                    {
                                        current_shape.x_index[0] = current_shape.x_index[0] - 1; current_shape.y_index[0] = current_shape.y_index[0] + 1;
                                        current_shape.x_index[1] = current_shape.x_index[1] + 0; current_shape.y_index[1] = current_shape.y_index[1] + 0;
                                        current_shape.x_index[2] = current_shape.x_index[2] + 1; current_shape.y_index[2] = current_shape.y_index[2] - 1;
                                        current_shape.x_index[3] = current_shape.x_index[3] - 1; current_shape.y_index[3] = current_shape.y_index[3] - 1;
                                        current_shape.rotate_degree = TWO_SEVENTY_DEGREE;
                                    }
                                }
                            }
                            /*   *****
                                 *   *
                                 * 2 *
                                 *   *
                             *********
                             *   *   *
                             * 3 * 1 *
                             *   *   *
                             *********
                                 *   *
                                 * 0 *
                                 *   *
                                 *****/
                            else if(current_shape.rotate_degree == TWO_SEVENTY_DEGREE)
                            {
                                if( (x+1) < GAMEBOARD_WIDTH )
                                {
                                    if(gameboard[y][x+1] == EMPTY)
                                    {
                                        current_shape.x_index[0] = current_shape.x_index[0] - 1; current_shape.y_index[0] = current_shape.y_index[0] - 1;
                                        current_shape.x_index[1] = current_shape.x_index[1] + 0; current_shape.y_index[1] = current_shape.y_index[1] + 0;
                                        current_shape.x_index[2] = current_shape.x_index[2] + 1; current_shape.y_index[2] = current_shape.y_index[2] + 1;
                                        current_shape.x_index[3] = current_shape.x_index[3] + 1; current_shape.y_index[3] = current_shape.y_index[3] - 1;
                                        current_shape.rotate_degree = ZERO_DEGREE;
                                    }
                                }
                            }
                            /*   *****
                                 *   *
                                 * 3 *
                                 *   *
                             *************
                             *   *   *   *
                             * 0 * 1 * 2 *
                             *   *   *   *
                             *************/
                            else console.log("ERROR in T shape: No such rotating degree!");
                            break;
                        }
                        case SHARP_Z:
                        {
                            var x = current_shape.x_index[1], y = current_shape.y_index[1];
                            /*********
                             *   *   *
                             * 0 * 2 *
                             *   *   *
                             *************
                                 *   *   *
                                 * 1 * 3 *
                                 *   *   *
                                 *********/
                            if(current_shape.rotate_degree == ZERO_DEGREE)
                            {
                                if( (y-1) >= 0 && (y+1) < GAMEBOARD_HEIGHT && (x+1) < GAMEBOARD_WIDTH )
                                if(gameboard[y+1][x] == EMPTY && gameboard[y-1][x+1] == EMPTY)
                                {
                                    current_shape.x_index[0] = current_shape.x_index[0] + 2; current_shape.y_index[0] = current_shape.y_index[0] + 0;
                                    current_shape.x_index[1] = current_shape.x_index[1] + 0; current_shape.y_index[1] = current_shape.y_index[1] + 0;
                                    current_shape.x_index[2] = current_shape.x_index[2] + 1; current_shape.y_index[2] = current_shape.y_index[2] + 1;
                                    current_shape.x_index[3] = current_shape.x_index[3] - 1; current_shape.y_index[3] = current_shape.y_index[3] + 1;
                                    current_shape.rotate_degree = NINETY_DEGREE;
                                }
                            }
                            /*   *****
                                 *   *
                                 * 0 *
                                 *   *
                             *********
                             *   *   *
                             * 1 * 2 *
                             *   *   *
                             *********
                               *   *
                             * 3 *
                             *   *
                             *****/
                            else if(current_shape.rotate_degree == NINETY_DEGREE)
                            {
                                if( (y-1) >= 0 && (x-1) >= 0 )
                                {
                                    if(gameboard[y-1][x] == EMPTY && gameboard[y-1][x-1] == EMPTY)
                                    {
                                        current_shape.x_index[0] = current_shape.x_index[0] - 2; current_shape.y_index[0] = current_shape.y_index[0] + 0;
                                        current_shape.x_index[1] = current_shape.x_index[1] + 0; current_shape.y_index[1] = current_shape.y_index[1] + 0;
                                        current_shape.x_index[2] = current_shape.x_index[2] - 1; current_shape.y_index[2] = current_shape.y_index[2] - 1;
                                        current_shape.x_index[3] = current_shape.x_index[3] + 1; current_shape.y_index[3] = current_shape.y_index[3] - 1;
                                        current_shape.rotate_degree = ZERO_DEGREE;
                                    }
                                }
                            }
                            /*********
                             *   *   *
                             * 0 * 2 *
                             *   *   *
                             *************
                                 *   *   *
                                 * 1 * 3 *
                                 *   *   *
                                 *********/
                            else console.log("ERROR in Z shape: No such rotating degree!");
                            break;
                        }
                    }
                }
                else if(keyIndex == DOWN)
                {        
                    move_down_a_line(playerIndex);
                }
                else if(keyIndex == LEFT)
                {
                    var isGoodToMoveLeft = true;
                    for(var i=0; i<NUMBER_OF_SQUARES_IN_SHAPE; i++)
                    {
                        var x = current_shape.x_index[i], y = current_shape.y_index[i];
                        if((x-1) < 0 || gameboard[y][x-1] != EMPTY)
                        {
                            isGoodToMoveLeft = false;
                            break;
                        }
                    }                    
                    if(isGoodToMoveLeft)
                    {
                        for(var i=0; i<NUMBER_OF_SQUARES_IN_SHAPE; i++)
                            current_shape.x_index[i]--;
                    }
                }            
                else if(keyIndex == RIGHT)
                {
                    var isGoodToMoveRight = true;
                    for(var i=0; i<NUMBER_OF_SQUARES_IN_SHAPE; i++)
                    {
                        var x = current_shape.x_index[i], y = current_shape.y_index[i];
                        if((x+1) >= GAMEBOARD_WIDTH || gameboard[y][x+1] != EMPTY)
                        {
                            isGoodToMoveRight = false;
                            break;
                        }
                    }                    
                    if(isGoodToMoveRight)
                    {
                        for(var i=0; i<NUMBER_OF_SQUARES_IN_SHAPE; i++)
                            current_shape.x_index[i]++;
                    }
                }
            }
        }
    };

    function move_down_a_line(playerIndex){
        var gameboard = players[playerIndex].gameboard;
        var current_shape = players[playerIndex].current_shape;
        
        var isGoodToMoveDown = true;
        for(var i=0; i<NUMBER_OF_SQUARES_IN_SHAPE; i++)
        {
            var x = current_shape.x_index[i], y = current_shape.y_index[i];
            if((y+1) >= GAMEBOARD_HEIGHT || gameboard[y+1][x] != EMPTY)
            {
                isGoodToMoveDown = false;
                break;
            }
        }

        if(isGoodToMoveDown)
        {
            for(var i=0; i<NUMBER_OF_SQUARES_IN_SHAPE; i++)
                current_shape.y_index[i]++;
        }
        else // the current shape is done, put the shape into the gameboard and load next shape
        {
            for(var i=0; i<NUMBER_OF_SQUARES_IN_SHAPE; i++)
            {
                var x = current_shape.x_index[i], y = current_shape.y_index[i];	
                gameboard[y][x] = current_shape.color;
            }

            // check whether renew shape array 
            // (not renew right now, just generated 50000 elements shape array in default, if it reaches the max, just loop again the array)
            
            // next shape
            players[playerIndex].shape_count = (players[playerIndex].shape_count+1) % NUMBER_OF_SHAPE_GENERATED;
            var next_shape_index = players[playerIndex].shape_count;
            // assign new shapes to the player
            players[playerIndex].current_shape = createShapeSquare(shape_array[next_shape_index]);
            players[playerIndex].next_shape = createShapeSquare(shape_array[players[playerIndex].shape_count + 1]);
            
            // check whether the user is isGameover
            var check_current_shape = players[playerIndex].current_shape;
            for(var i=0; i<NUMBER_OF_SQUARES_IN_SHAPE; i++)
            {
                var x = check_current_shape.x_index[i], y = check_current_shape.y_index[i];
                if(gameboard[y][x] != EMPTY)
                {
                    players[playerIndex].isGameover = true;
                    for(var j=0; j<NUMBER_OF_SQUARES_IN_SHAPE; j++)
                        gameboard[check_current_shape.y_index[j]][check_current_shape.x_index[j]] = check_current_shape.color;
                    break;
                }
            }
            // check whether the lines can be eliminated
            if(!players[playerIndex].isGameover)
            {
                var eliminatedLines = 0;
                var checkNumOfLines = GAMEBOARD_HEIGHT - 1;
                while(true)
                {
                    if(checkNumOfLines < 0) break;
                    
                    var i = checkNumOfLines;
                    
                    var isLineFull = true;
                    for(var j=0; j<GAMEBOARD_WIDTH; j++) // check the specific row
                    {
                        if(gameboard[i][j] == EMPTY)
                        {
                            isLineFull = false;
                            break;
                        }
                    }
                    
                    if(isLineFull)
                    {
                        // move all rows that is above the eliminated line a line down
                        for(var m=i-1; m>=0; m--)
                        {
                            for(var n=0; n<GAMEBOARD_WIDTH; n++)
                                gameboard[m+1][n] = gameboard[m][n];
                        }
                        
                        for(var k=0; k<GAMEBOARD_WIDTH; k++) gameboard[0][k] = EMPTY; // the squares of the top row set to empty
                        
                        eliminatedLines++;
                    }
                    else checkNumOfLines--; // check next line
                }
                players[playerIndex].lines += eliminatedLines;
                if(eliminatedLines == 4) players[playerIndex].tetris++;
                // adjust level
                if(players[playerIndex].level < 20)
                {
                    players[playerIndex].next_level_line_count += eliminatedLines;
                    if(players[playerIndex].next_level_line_count >= NEXT_LEVEL_REQUIRED_LINES[players[playerIndex].level])
                    {
                        players[playerIndex].next_level_line_count -= NEXT_LEVEL_REQUIRED_LINES[players[playerIndex].level];
                        players[playerIndex].level++;
                        players[playerIndex].update_game_time = 0;
                    }
                }
            }
            // check whether all players are isGameover
            if(!isDual){
                if(players[0].isGameover) current_game_state = GAME_STATE_GAMEOVER
            }
            else {
                var isAllPlayerGameover = true;
                for(var idx=0; idx < players.length; idx++)
                {
                    if(!players[idx].isGameover)
                    {
                        isAllPlayerGameover = false;
                        break;
                    }
                }
                if(isAllPlayerGameover) current_game_state = GAME_STATE_GAMEOVER
            }
        }
    };
    
    function createShapeSquare(shape_data){
        var shape = new SHAPE();
        shape.color = shape_data.color;
        shape.type_of_shape = shape_data.shape;
        switch(shape.type_of_shape)
        {
            case SHARP_I:
                shape.x_index[0] = SHAPE_INITIAL_X_INDEX + 0; shape.y_index[0] = SHAPE_INITIAL_Y_INDEX + 0;
                shape.x_index[1] = SHAPE_INITIAL_X_INDEX + 1; shape.y_index[1] = SHAPE_INITIAL_Y_INDEX + 0;
                shape.x_index[2] = SHAPE_INITIAL_X_INDEX + 2; shape.y_index[2] = SHAPE_INITIAL_Y_INDEX + 0;
                shape.x_index[3] = SHAPE_INITIAL_X_INDEX + 3; shape.y_index[3] = SHAPE_INITIAL_Y_INDEX + 0;
                break;
            case SHARP_J:
                shape.x_index[0] = SHAPE_INITIAL_X_INDEX + 1; shape.y_index[0] = SHAPE_INITIAL_Y_INDEX + 1;
                shape.x_index[1] = SHAPE_INITIAL_X_INDEX + 2; shape.y_index[1] = SHAPE_INITIAL_Y_INDEX + 1;
                shape.x_index[2] = SHAPE_INITIAL_X_INDEX + 3; shape.y_index[2] = SHAPE_INITIAL_Y_INDEX + 1;
                shape.x_index[3] = SHAPE_INITIAL_X_INDEX + 1; shape.y_index[3] = SHAPE_INITIAL_Y_INDEX + 0;
                break;
            case SHARP_L:
                shape.x_index[0] = SHAPE_INITIAL_X_INDEX + 1; shape.y_index[0] = SHAPE_INITIAL_Y_INDEX + 1;
                shape.x_index[1] = SHAPE_INITIAL_X_INDEX + 2; shape.y_index[1] = SHAPE_INITIAL_Y_INDEX + 1;
                shape.x_index[2] = SHAPE_INITIAL_X_INDEX + 3; shape.y_index[2] = SHAPE_INITIAL_Y_INDEX + 1;
                shape.x_index[3] = SHAPE_INITIAL_X_INDEX + 3; shape.y_index[3] = SHAPE_INITIAL_Y_INDEX + 0;
                break;
            case SHARP_O:
                shape.x_index[0] = SHAPE_INITIAL_X_INDEX + 1; shape.y_index[0] = SHAPE_INITIAL_Y_INDEX + 0;
                shape.x_index[1] = SHAPE_INITIAL_X_INDEX + 2; shape.y_index[1] = SHAPE_INITIAL_Y_INDEX + 0;
                shape.x_index[2] = SHAPE_INITIAL_X_INDEX + 1; shape.y_index[2] = SHAPE_INITIAL_Y_INDEX + 1;
                shape.x_index[3] = SHAPE_INITIAL_X_INDEX + 2; shape.y_index[3] = SHAPE_INITIAL_Y_INDEX + 1;
                break;
            case SHARP_S:
                shape.x_index[0] = SHAPE_INITIAL_X_INDEX + 1; shape.y_index[0] = SHAPE_INITIAL_Y_INDEX + 1;
                shape.x_index[1] = SHAPE_INITIAL_X_INDEX + 2; shape.y_index[1] = SHAPE_INITIAL_Y_INDEX + 1;
                shape.x_index[2] = SHAPE_INITIAL_X_INDEX + 2; shape.y_index[2] = SHAPE_INITIAL_Y_INDEX + 0;
                shape.x_index[3] = SHAPE_INITIAL_X_INDEX + 3; shape.y_index[3] = SHAPE_INITIAL_Y_INDEX + 0;
                break; 
            case SHARP_T:
                shape.x_index[0] = SHAPE_INITIAL_X_INDEX + 1; shape.y_index[0] = SHAPE_INITIAL_Y_INDEX + 1;
                shape.x_index[1] = SHAPE_INITIAL_X_INDEX + 2; shape.y_index[1] = SHAPE_INITIAL_Y_INDEX + 1;
                shape.x_index[2] = SHAPE_INITIAL_X_INDEX + 3; shape.y_index[2] = SHAPE_INITIAL_Y_INDEX + 1;
                shape.x_index[3] = SHAPE_INITIAL_X_INDEX + 2; shape.y_index[3] = SHAPE_INITIAL_Y_INDEX + 0;
                break; 
            case SHARP_Z:
                shape.x_index[0] = SHAPE_INITIAL_X_INDEX + 1; shape.y_index[0] = SHAPE_INITIAL_Y_INDEX + 0;
                shape.x_index[1] = SHAPE_INITIAL_X_INDEX + 2; shape.y_index[1] = SHAPE_INITIAL_Y_INDEX + 1;
                shape.x_index[2] = SHAPE_INITIAL_X_INDEX + 2; shape.y_index[2] = SHAPE_INITIAL_Y_INDEX + 0;
                shape.x_index[3] = SHAPE_INITIAL_X_INDEX + 3; shape.y_index[3] = SHAPE_INITIAL_Y_INDEX + 1;
                break; 
            default: alert("ERROR: TYPE OF SHAPE NOT FOUND!");
        }
        
        return shape;
    };
    
    /**
    Length of a tick in milliseconds. The denominator is your desired framerate.
    e.g. 1000 / 20 = 20 fps,  1000 / 60 = 60 fps
    */
    var tickLengthMs = 1000 / 60

    /* gameLoop related variables */
    // timestamp of each loop
    var previousTick = Date.now()
    // number of times gameLoop gets called
    var actualTicks = 0

    var gameLoop = function () {
        var now = Date.now()

        actualTicks++
        if (previousTick + tickLengthMs <= now) {
            var delta = (now - previousTick) / 1000
            previousTick = now

            update(delta)

            // console.log('delta', delta, '(target: ' + tickLengthMs +' ms)', 'node ticks', actualTicks)
            actualTicks = 0
        }

        setTimeout(gameLoop)
    };

    // The following code copied from https://github.com/timetocode/node-game-loop/blob/master/gameLoop.js with MIT Licence
    /**
    Update is normally where all of the logic would go. In this case we simply call
    a function that takes 10 milliseconds to complete thus simulating that our game
    had a very busy time.
    */
    var update = function(delta) {
        // print frame time infomation on console for dubug purpose
        //console.log(update_frame_time);
        update_frame_time++;
        update_frame_time %= FPS;
        
        if(current_game_state == GAME_STATE_PLAYING)
        {
            for(var idx=0; idx<TOTAL_PLAYERS; idx++)
            {
                players[idx].update_game_time++;
                var current_level = players[idx].level;
                if(!players[idx].isGameover)
                {
                    if(players[idx].update_game_time >= LEVEL_GAME_UPDATE_TIME[current_level])
                    {
                        move_down_a_line(idx);   // force the shape moves down a line
                        players[idx].update_game_time = 0;
                    }
                }
            }
        }
        
        renewUI()
    };
    
    gameLoop()
</script>
</body>
</html>